import java.lang.Comparable;
import java.lang.IndexOutOfBoundsException;
import java.lang.NullPointerException;
import java.lang.UnsupportedOperationException;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;


public class TupleUtil {
    public interface Tuple<T> extends Iterable<T> {
        /**
         * Gets the element at the given index in the tuple
         *
         * @param index the index of the element to be returned
         * @return the element at the given index in the tuple
         * @throws IndexOutOfBoundsException if the element is negative or greater than the last element index
         */
        T get(int index);
    
        /**
         * Returns a Tuple with the element at the given index replaced with the given value
         *
         * @param index the index of the element to be replace
         * @param value the value with which to replace the given element
         * @return a tuple with the element at the given index replaced with the given value
         * @throws IndexOutOfBoundsException if the element is negative or greater than the last element index
         */
        Tuple<T> replace(int index, T value);
    
        /**
         * Returns the number of elements in this tuple
         * @return the number of elements in this tuple
         */
        int size();
    
        /**
         * Returns an array containing the elements of this tuple in order
         * @return an array containing the elements of this tuple in order
         */
        Object[] toArray();
    
        /**
         * Returns an iterator over the elements of this tuple in order
         * @return an iterator over the elements of this tuple in order
         */
        @Override
        Iterator<T> iterator();
    
        /**
         * Indicates whether some other object is equal to this Tuple.
         *
         * Two Tuples are considered equal if all both contain the same number of elements which are
         * {@link Object#equals(Object)} to each other (or both null), in the same order.
         *
         * @param o the object to check for equality
         * @return true if the object is equal to this Tuple, false otherwise
         */
        @Override
        boolean equals(Object o);
    
        /**
         * Returns a hash code value for the tuple which meets the "general contract" of {@link Object#hashCode()}
         *
         * @return a hash code value for the tuple which meets the "general contract" of {@link Object#hashCode()}
         */
        @Override
        int hashCode();
    }
    
    public static class MyTuple<T> implements Tuple<T> {
        final private Object[] m_arrs;
        public <T> MyTuple(final T... params) {
            m_arrs = new Object[params.length];
            for (int i=0;i<params.length;i++){
                m_arrs[i] = params[i];
            }
        }

        @Override
        public T get(int index) {
            if (index >= m_arrs.length || index < 0) {
                throw new IndexOutOfBoundsException();
            }
            return (T)m_arrs[index];
        }

        @Override
        public Tuple<T> replace(int index, T value) {
            if ((index >= m_arrs.length || index < 0)) {
                throw new IndexOutOfBoundsException();
            }
            Object[] tmpArr = new Object[m_arrs.length];
            for(int i = 0;i < m_arrs.length;i++) {
                if (index == i) {
                    tmpArr[i] = value;
                } else {
                    tmpArr[i] = m_arrs[i];
                }
            }
            return new MyTuple(tmpArr);
        }

        @Override
        public int size() {
            return m_arrs.length;
        }

        @Override
        public Object[] toArray() {
            return m_arrs.clone();
        }
    
        @Override
        public Iterator<T> iterator() {
            class TupleIter  implements Iterator<T> {
                private int curIdx = 0;
            
                @Override
                public boolean hasNext() {
                    return curIdx != m_arrs.length;
                }

                @Override
                public T next() {
                    T item = (T)m_arrs[curIdx];
                    curIdx++;
                    return item;
                }

                @Override
                public void remove() {
                    throw new UnsupportedOperationException();
                }
            }
            return new TupleIter();
        }

        @Override
        public boolean equals(Object o) {
            if (o == this) {
                return true;
            }
            if (!(o instanceof MyTuple)) {
                return false;
            }
            MyTuple rh = (MyTuple)o;
            if (size() != rh.size()) {
                return false;
            }
            for (int i = 0; i < size(); i++) {
                if (get(i) != null) {
                    if (!get(i).equals(rh.get(i))) {
                        return false;
                    }
                } else if (rh.get(i) != null) {
                    return false;
                }
            }
            return true;
        }

        @Override
        public int hashCode() {
            int result = 17;
            for (int i = 0; i < m_arrs.length; i++) {
                if (m_arrs[i] != null) {
                    result = result * 37 + m_arrs[i].hashCode(); 
                } else {
                    result = result * 37 + i;
                }
            }
            return result; 
        }
    }

    public interface ComparableTuple<T extends Comparable<T>> extends Tuple<T>, Comparable<ComparableTuple<T>> {
        /**
         * Compares this ComparableTuple with the specified ComparableTuple for order.  Returns a negative integer, zero,
         * or a positive integer as this ComparableTuple is less than, equal to, or greater than the specified ComparableTuple.
         *
         * ComparableTuples are ordered lexicographically. Two ComparableTuples can be different for two reasons:
         *
         * <ol>
         *     <li>
         *         One of more of the elements at a given index is different from the element at the same index in the
         *         other tuple
         *     </li>
         *     <li>
         *         The two tuples contain a different number of elements
         *     </li>
         * </ol>
         *
         * In the first case, if the first different element in this tuple is less than that of the of the corresponding
         * element in the other tuple, then this tuple is less than the other tuple; otherwise it is greater than the other
         * tuple. Note that null elements are considered less than non-null elements.
         *
         * In the second case, if elements at common indices are equal, then the shorter tuple is less than the longer
         * tuple.
         *
         * @param o the tuple with which to compare this tuple
         * @return a negative integer, zero, or a positive integer as this tuple
         *          is less than, equal to, or greater than the specified tuple
         */
        @Override
        int compareTo(ComparableTuple<T> o);
    }

    public static class MyCompareTuple<T extends Comparable<T> > implements ComparableTuple<T> {
        final private Object[] m_arrs;
        public <T extends Comparable<T> > MyCompareTuple(final T... params) {
            m_arrs = new Object[params.length];
            for (int i=0;i<params.length;i++){
                m_arrs[i] = (Comparable<T>)params[i];
            }
        }

        public <T extends Comparable<T> > MyCompareTuple(final Collection<T> collection) {
            m_arrs = new Object[collection.size()];
            int i = 0;
            for(Object a : collection) {
                m_arrs[i++] = a;
            }
        }

        @Override
        public T get(int index) {
            return (T)m_arrs[index];
        }

        @Override
        public Tuple<T> replace(int index, T value) {
            Object[] tmpArr = new Object[m_arrs.length];
            for(int i = 0;i < m_arrs.length;i++) {
                if (index == i) {
                    tmpArr[i] = value;
                } else {
                    tmpArr[i] = m_arrs[i];
                }
            }
            return new MyTuple(tmpArr);
        }

        @Override
        public int size() {
            return m_arrs.length;
        }

        @Override
        public Object[] toArray() {
            return m_arrs.clone();
        }
    
        @Override
        public Iterator<T> iterator() {
            class TupleIter  implements Iterator<T> {
                private int curIdx = 0;
            
                @Override
                public boolean hasNext() {
                    return curIdx != m_arrs.length;
                }

                @Override
                public T next() {
                    T item = (T)m_arrs[curIdx];
                    curIdx++;
                    return item;
                }

                @Override
                public void remove() {
                    throw new UnsupportedOperationException();
                }
            }
            return new TupleIter();
        }

        @Override
        public boolean equals(Object o) {
            if (o == this) {
                return true;
            }
            if (!(o instanceof MyTuple)) {
                return false;
            }
            MyTuple rh = (MyTuple)o;
            if (size() != rh.size()) {
                return false;
            }
            for (int i = 0; i < size(); i++) {
                if (get(i) != null) {
                    if (!get(i).equals(rh.get(i))) {
                        return false;
                    }
                } else if (rh.get(i) != null) {
                    return false;
                }
            }
            return true;
        }

        @Override
        public int hashCode() {
            int result = 17;
            for (int i = 0; i < m_arrs.length; i++) {
                if (m_arrs[i] != null) {
                    result = result * 37 + m_arrs[i].hashCode(); 
                } else {
                    result = result * 37 + i;
                }
            }
            return result; 
        }

        @Override
        public int compareTo(ComparableTuple<T> o) {
            int len = (size() < o.size() ? size() : o.size());
            for (int i = 0; i < len; i++) {
                if (get(i) == o.get(i)) {
                    continue;
                }
                if (get(i) == null && o.get(i) != null) {
                    return -1;
                } else if (get(i) != null && o.get(i) == null) {
                    return 1;
                }
                if (get(i).getClass() == o.get(i).getClass()) {
                    return get(i).compareTo(o.get(i));
                } else {
                    return 0;
                }
            }
            if (size() > o.size()) {
                return 1;
            } else if (size() < o.size()) {
                return -1;
            } else {
                return 0;
            }
        }
    }


    public interface TupleFactory {
        /**
         * Returns a new Tuple containing the elements in the given array in the same order
         * @param arr the array containing the Tuple's elements
         * @param <T> the lower bound of the Tuple elements types
         * @return a new Tuple containing the elements in the given array in the same order
         * @throws java.lang.NullPointerException if the array is null
         */
        <T> Tuple<T> newTuple(T... arr);
    
        /**
         * Returns a new Tuple containing the elements in the given collection in the same order
         * @param collection the collection containing the Tuple's elements
         * @param <T> the lower bound of the Tuple elements types
         * @return a new Tuple containing the elements in the given collection in the same order
         * @throws java.lang.NullPointerException if the collection is null
         */
        <T> Tuple<T> newTuple(Collection<T> collection);
    
        /**
         * Returns a new {@link com.eclipseoptions.javatest.tuple.api.ComparableTuple} containing the comparable elements
         * in the given array in the same order.
         *
         * @param arr the array containing the Tuple's elements
         * @param <T> the lower bound of the Tuple elements types
         * @return a comparable Tuple containing the comparable elements in the given array in the same order
         * @throws java.lang.NullPointerException if the array is null
         */
        <T extends Comparable<T>> ComparableTuple<T> newComparableTuple(final T... arr);
    
        /**
         * Returns a new {@link com.eclipseoptions.javatest.tuple.api.ComparableTuple} containing the comparable elements
         * in the given collection in the same order.
         *
         * @param collection the collection containing the Tuple's elements
         * @param <T> the lower bound of the Tuple elements types
         * @return a comparable Tuple containing the comparable elements in the given collection in the same order
         * @throws java.lang.NullPointerException if the collection is null
         */
        <T extends Comparable<T>> ComparableTuple<T> newComparableTuple(Collection<T> collection);
    }

    public static class MyTupleFactory implements TupleFactory {
        @Override
        public <T> Tuple<T> newTuple(T... arr) {
            if(arr == null) {
                throw new NullPointerException();
            }
            return new MyTuple(arr);
        }
    
        @Override
        public <T> Tuple<T> newTuple(Collection<T> collection) {
            if(collection == null) {
                throw new NullPointerException();
            }
            return new MyTuple(collection.toArray());
        }
    
        @Override
        public <T extends Comparable<T>> ComparableTuple<T> newComparableTuple(final T... arr) {
            if(arr == null) {
                throw new NullPointerException();
            }
            return new MyCompareTuple(arr);
        }

        @Override
        public <T extends Comparable<T>> ComparableTuple<T> newComparableTuple(Collection<T> collection) {
            if(collection == null) {
                throw new NullPointerException();
            }
            return new MyCompareTuple(collection);
        }
    }
}